﻿using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Reflection;

namespace Eswat.PowerShell
{
	/// <summary>
	/// Test runner for PowerShell test scripts.
	/// </summary>
	public class PowerShellTestRunner : TestRunnerBase<IEnumerable<PSObject>>, IDisposable
	{
		private Runspace _runspace;

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Called when [before test].
		/// </summary>
		/// <typeparam name="TTestContext">The type of the test context.</typeparam>
		/// <param name="testContext">The test context.</param>
		/// <param name="availableInMemoryAssemblies">The available in memory assemblies.</param>
		protected override void OnBeforeTest<TTestContext>(
			ITestContext<TTestContext> testContext,
			IEnumerable<Assembly> availableInMemoryAssemblies)
		{
			this._runspace = RunspaceFactory.CreateRunspace();

			foreach (var assembly in availableInMemoryAssemblies)
			{
				var assemblyConfig = new AssemblyConfigurationEntry(assembly.FullName, assembly.Location);

				this._runspace.RunspaceConfiguration.Assemblies.Append(assemblyConfig);
			}
		}

		/// <summary>
		/// Runs the test.
		/// </summary>
		/// <typeparam name="TTestContext">The type of the test context.</typeparam>
		/// <param name="testContext">The test context.</param>
		/// <returns>
		/// The test result.
		/// </returns>
		protected override IEnumerable<PSObject> RunTest<TTestContext>(ITestContext<TTestContext> testContext)
		{
			this._runspace.Open();

			this._runspace.SessionStateProxy.SetVariable("EswatTest", testContext);

			var pipeline = this._runspace.CreatePipeline();

			pipeline.Commands.AddScript(testContext.Template);

			var results = pipeline.Invoke();

			this._runspace.Close();

			return results;
		}

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (this._runspace != null) this._runspace.Dispose();
			}
		}

		/// <summary>
		/// Releases unmanaged resources and performs other cleanup operations before the
		/// <see cref="PowerShellTestRunner"/> is reclaimed by garbage collection.
		/// </summary>
		~PowerShellTestRunner()
		{
			this.Dispose(false);
		}
	}
}
